package com.xuef.controller;

import com.google.common.collect.Lists;
import com.xuef.base.ApiResponse;
import com.xuef.base.Cons;
import com.xuef.entity.Blog;
import com.xuef.entity.Catalog;
import com.xuef.entity.EsBlog;
import com.xuef.entity.User;
import com.xuef.service.*;
import com.xuef.service.impl.QiniuServiceImpl;
import com.xuef.vo.GroupBlogVO;
import com.xuef.vo.TagVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import java.io.IOException;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 用户主页
 * Created by moveb on 2018/5/7.
 */
@Controller
@RequestMapping("/space")
public class UserspaceController {
    private Logger logger = LoggerFactory.getLogger(UserspaceController.class);

    /**
     * 注入IOC容器的应该是我自己的实现类 AuthServiceImpl 的实例
     */
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private BlogService blogService;

    @Autowired
    private CatalogService catalogService;

    @Autowired
    private ILikeService likeService;

    @Autowired
    private IUserService userService;

    @Autowired
    private QiniuServiceImpl qiniuService;

    @Autowired
    private EsBlogService esBlogService;

    // 请求跳转
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    /**
     * 访问用户主页
     * @param username
     * @param model
     * @return
     */
    @GetMapping("/{username}")
    public String userSpace(@PathVariable("username") String username, Model model){
        User user = (User)userDetailsService.loadUserByUsername(username);
        model.addAttribute("user", user);
        logger.info("访问博客主页用户信息：" + user);
        return "redirect:/space/" + username + "/blogs";
    }

    @GetMapping("/{username}/profile")
    @PreAuthorize("authentication.name.equals(#username)")
    public ModelAndView profile(@PathVariable("username") String username, Model model) {
        User  user = (User)userDetailsService.loadUserByUsername(username);
        model.addAttribute("user", user);
        return new ModelAndView("/userspace/profile", "userModel", model);
    }

    /**
     * 保存个人设置
     * @param user
     * @return
     */
    @PostMapping("/{username}/profile")
    @PreAuthorize("authentication.name.equals(#username)")
    public String saveProfile(@PathVariable("username") String username,User user) {
        User originalUser = userService.getUserById(user.getId());
        originalUser.setEmail(user.getEmail());
        originalUser.setName(user.getName());

        // 判断密码是否做了变更
        String rawPassword = originalUser.getPassword();
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        String encodePasswd = encoder.encode(user.getPassword());
        boolean isMatch = encoder.matches(rawPassword, encodePasswd);
        if (!isMatch) {
            // 加密密码
            originalUser.setPassword(encoder.encode(user.getPassword()));
        }

        userService.saveUser(originalUser);
        return "redirect:/u/" + username + "/profile";
    }

    /**
     * 获取编辑头像的界面
     * @param username
     * @param model
     * @return
     */
    @GetMapping("/{username}/avatar")
    @PreAuthorize("authentication.name.equals(#username)")
    public ModelAndView avatar(@PathVariable("username") String username, Model model) {
        User  user = (User)userDetailsService.loadUserByUsername(username);
        model.addAttribute("user", user);
        return new ModelAndView("/userspace/avatar", "userModel", model);
    }


    /**
     * 保存头像
     * @param username
     * @return
     */
    @PostMapping("/{username}/avatar")
    @PreAuthorize("authentication.name.equals(#username)")
    public ResponseEntity<ApiResponse> saveAvatar(@PathVariable("username") String username,
                                                  MultipartFile file) {
        String avatarUrl = "";
        try {
            avatarUrl = qiniuService.upload(file);
        } catch (IOException e) {
            logger.info("上传头像失败");
            return ResponseEntity.badRequest().body(ApiResponse.ofMessage(500, "upload failed"));
        }
        User user = userService.findByUsername(username);

        user.setHeadUrl(avatarUrl);
        userService.saveUser(user);

        return ResponseEntity.ok().body(ApiResponse.ofSuccess(avatarUrl));
    }

    @GetMapping("/{username}/search")
    public String search(@PathVariable("username") String username,
                         @RequestParam("keyword") String keyword,
                         @RequestParam(value="pageIndex",required=false,defaultValue="0") int pageIndex,
                         @RequestParam(value="pageSize",required=false,defaultValue="10") int pageSize,
                         Model model){

        User  user = (User)userDetailsService.loadUserByUsername(username);
        Pageable pageable = new PageRequest(pageIndex, pageSize);
        Page<EsBlog> pageEs = esBlogService.fullTextSearch4OneUser(
                username,
                    keyword,
                    keyword,
                    keyword,
                    keyword,
                    pageable
            );
        List<EsBlog> esBlogs = pageEs.getContent();
        model.addAttribute("page", pageEs);
        /**
         * 因为user 没有映射进esBlog,所以在页面显示上有问题
         * 在下一版中，通过Blog和EsBlog更一致的映射来解决问题
         * 这里再包装一下
         */
        List<Integer> blogIds = Lists.newArrayList();
        for(EsBlog eb: pageEs){
            blogIds.add(eb.getBlogId());
        }

        Page<Blog> blogList = blogService.findByIdIn(blogIds, pageable);

        model.addAttribute("user", user);
        model.addAttribute("blogList", blogList);
        return "userspace/u :: #mainContainerRepleace";
    }

    /**
     * 根据需要获取博客列表
     * @param username 用户名
     * @param order 指定博客列表排序方式
     * @param catalogId 博客类别
     * @param keyword 关键词
     * @param async 是否是异步请求 默认为否
     * @param pageIndex 第几页
     * @param pageSize 每页条数
     * @param model 存储返回数据的model
     * @return
     */
    @GetMapping("/{username}/blogs")
    public String listBlogsByOrder(@PathVariable("username") String username,
                                   @RequestParam(value="order",required=false,defaultValue="new") String order,
                                   @RequestParam(value="catalog",required=false ) Integer catalogId,
                                   @RequestParam(value="keyword",required=false,defaultValue="" ) String keyword,
                                   @RequestParam(value ="type", required = false, defaultValue = "") String type, // keyword 类型
                                   @RequestParam(value="async",required=false) boolean async,
                                   @RequestParam(value="pageIndex",required=false,defaultValue="0") int pageIndex,
                                   @RequestParam(value="pageSize",required=false,defaultValue="10") int pageSize,
                                   Model model,
                                   Authentication authentication) {
        // ??? 博主的详细信息
        User  user = (User)userDetailsService.loadUserByUsername(username);

        Page<Blog> page = null;

        if (catalogId != null && catalogId > 0) { // 分类查询
            Catalog catalog = catalogService.getCatalogById(catalogId);
            Pageable pageable = new PageRequest(pageIndex, pageSize);
            page = blogService.listBlogsByCatalog(catalog, pageable);
            order = "";
        } else if (order.equals("hot")) { // 最热查询
            Sort sort = new Sort(Sort.Direction.DESC,"readSize","commentSize","voteSize");
            Pageable pageable = new PageRequest(pageIndex, pageSize, sort);
            page = blogService.listBlogsByTitleVoteAndSort(user, keyword, pageable);
        } else if (order.equals("new") && type.equals("")) {
            // 查询具体用户博客，如果有title或者标签，将做模糊匹配。最后按时间降序
            Pageable pageable = new PageRequest(pageIndex, pageSize);
            page = blogService.listBlogsByUserAndKeywordLike(user, keyword, pageable);
        }else if(type.equals("yymm")){
            // 按年月查询博客
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
            Calendar createTime = Calendar.getInstance();
            try {
                createTime.setTime(simpleDateFormat.parse(keyword));
                String startTimeOfThisMonth = keyword + "-01 00:00:00";
                simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                // 该月最后一天
                createTime.set(Calendar.DAY_OF_MONTH, createTime.getActualMaximum(Calendar.DAY_OF_MONTH));

                String endTimeOfThisMonth = keyword + "-" + createTime.get(Calendar.DATE) + " 23:59:59";

                //logger.info("starttime: " + startTimeOfThisMonth + "end: " + endTimeOfThisMonth);
                Date start = simpleDateFormat.parse(startTimeOfThisMonth);
                Date end = simpleDateFormat.parse(endTimeOfThisMonth);

                //logger.info("start: " + start + "---end: " + end);
                Pageable pageable = new PageRequest(pageIndex, pageSize);
                page = blogService.listBlogsByYearMonth(user, start, end, pageable);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }

        List<Blog> list = null;

        if(page != null) {
            list = page.getContent();    // 当前所在页面数据列表
        }

        if(async == false) {
            // 获取用户热门标签
            List<TagVO> userTags = esBlogService.listTop30Tags(username);
            List<TagVO> newUserTags = Lists.newArrayList();
            // 去除单字符标签
            for (TagVO tag : userTags) {
                if (tag.getName().length() > 1) {
                    newUserTags.add(tag);
                }
            }

            // 将用户博客按年月归档
            List<Object[]> result = blogService.getBlogCount(user.getId());

            Map<Date, Long> map = null;
            List<GroupBlogVO> timeDoc = Lists.newArrayList();
            if (result != null && !result.isEmpty()) {
                for (Object[] object : result) {
//                logger.info(object[0] + "-------" + object[1]);
                    // count(*) 为什么会返回 BigInteger???
                    timeDoc.add(new GroupBlogVO((String) object[0], ((BigInteger) object[1]).intValue()));
                }
            }

            model.addAttribute("tags", newUserTags);
            model.addAttribute("timeDoc", timeDoc);
        }
        // 判断访问者是否是博客的所有者
        User principal = null;
        boolean isBlogOwner = false;
        if (authentication !=null && authentication.isAuthenticated()){
            if (username.equals(authentication.getName())) {
                isBlogOwner = true;
            }
        }

        model.addAttribute("isBlogOwner", isBlogOwner);

        //logger.info("归档博客： " + timeDoc);
        model.addAttribute("user", user);
        model.addAttribute("order", order);
        model.addAttribute("catalogId", catalogId);
        model.addAttribute("keyword", keyword);
        model.addAttribute("page", page);
        model.addAttribute("blogList", list);

        model.addAttribute("blogListTitle", "博客文章列表");
        // 设置博客阅读量
        for(Blog b:list){
            b.setReadCount(blogService.getReadCount(b.getId()));
        }

        if(authentication != null) {
            model.addAttribute("username_authenticated", authentication.getName());
        }else{
            model.addAttribute("username_authenticated", "nil");
        }
        // 根据请求方式来决定使用模板引擎的方式
        // 如果是异步请求，就
        return (async==true?"userspace/u :: #mainContainerRepleace":"userspace/u");
    }

    @GetMapping("/{username}/likedBlogs")
    public String likedBlogs(@PathVariable("username") String username,
                             Model model){
        User user = userService.findByUsername(username);
        int userId = 0;
        if(user != null){
            userId = user.getId();
        }
        Set<String> likedBlogIds = likeService.getLikedBlogs(userId);
        List<Integer> ids = Lists.newArrayList();
        if(likedBlogIds != null){
            for(String id:likedBlogIds){
                ids.add(Integer.parseInt(id));
            }
        }
        int pageIndex = 0, pageSize = 10;

        Pageable pageable = new PageRequest(pageIndex, pageSize);
        Page<Blog> likedBlogs = blogService.findByIdIn(ids, pageable);

        for(Blog blog:likedBlogs){
            blog.setReadCount(blogService.getReadCount(blog.getId()));
        }
        model.addAttribute("blogListTitle", "喜欢的文章");
        model.addAttribute("blogList", likedBlogs.getContent());
        model.addAttribute("page", likedBlogs);
        model.addAttribute("user", user);
        // 使用数据渲染 userspace/u 中的mainContainerRepleace部分，然后将渲染好的html返回
        return "userspace/u :: #mainContainerRepleace";
    }
    /**
     * 获取新增博客的界面
     * 获取新增博客界面需要用户登录?
     * @param model
     * @return
     */
    @GetMapping("/{username}/blogs/edit")
    @PreAuthorize("authentication.name.equals(#username)")
    public ModelAndView createBlog(@PathVariable("username") String username,
                                   Model model,
                                   Authentication authentication) {
        // 说明未登录
        if(authentication == null || !authentication.isAuthenticated()){
            // 跳到登录界面(认证界面)
            // 此处可以去掉；我已经在securityconfig中添加了对/space/*/blogs/edit的拦截
            // 所以未登录前已经做了处理，不会到这里。
            return new ModelAndView("redirect:/authentication/require");
        }
        User user = (User)userDetailsService.loadUserByUsername(username);
        List<Catalog> catalogs = catalogService.listCatalogs(user);
        logger.info("类别有： " + catalogs);
        logger.info("授权信息： " + authentication.getName());

        model.addAttribute("blog", new Blog(null, null, null));
        model.addAttribute("catalogs", catalogs);

        if(authentication != null) {
            model.addAttribute("username_authenticated", authentication.getName());
        }else{
            model.addAttribute("username_authenticated", "nil");
        }
        // nav中需要 user.username 有什么更好的办法
        model.addAttribute("user", user);
        return new ModelAndView("userspace/blogedit", "blogModel", model);
    }

    /**
     * 保存博客
     * @param username
     * @param blog
     * @return
     */
    @PostMapping("/{username}/blogs/edit")
    @PreAuthorize("authentication.name.equals(#username)")
    public ResponseEntity<ApiResponse> saveBlog(@PathVariable("username") String username,
                                                @RequestBody Blog blog){
        // 新增还是修改博客
        if (blog.getId()!=null) {
            // 修改
            Blog orignalBlog = blogService.getBlogById(blog.getId());
            orignalBlog.setTitle(blog.getTitle());
            orignalBlog.setContent(blog.getContent());
            orignalBlog.setSummary(blog.getSummary());
            orignalBlog.setCatalog(blog.getCatalog());
            orignalBlog.setTags(blog.getTags());
            // 保存博文；更新到mysql数据库和elasticsearch文库
            blog = blogService.saveBlog(orignalBlog);
        } else {
            // 新增博文
            User user = (User)userDetailsService.loadUserByUsername(username);
            blog.setUser(user);
            blog = blogService.saveBlog(blog);
        }
        //logger.info("--------------博客信息：" + blog);
        // TODO 异常处理

        // 博客发布成功好，转到该博文展示页
        String redirectUrlAfterSuccess = "/space/" + username + "/blogs/" + blog.getId(); // 当id为null时，会抛异常
        logger.info("edit后跳转到: " + redirectUrlAfterSuccess);
        return ResponseEntity.ok().body(ApiResponse.ofSuccess(redirectUrlAfterSuccess));
    }

    /**
     * 根据 blog id 查看某用户的某篇博客
     * @param id
     * @param model
     * @return
     */
    @GetMapping("/{username}/blogs/{id}")
    public String getBlogById(@PathVariable("username") String username,
                              @PathVariable("id") Integer id,
                              Model model,
                              Authentication authentication) {
        // ??? 博主的详细信息
        User  user = (User)userDetailsService.loadUserByUsername(username);

        User principal = null;
        Blog blog = blogService.getBlogById(id);
        // 每次读取，简单的可以认为阅读量增加1次
        // 如果并发量大的话，如此频繁的update数据库，会有问题。
        blogService.readingIncrease(id);
        blog.setReadCount(blogService.getReadCount(id));

        // 如果用户登录，设定用户与本篇博文的关系。此处设置是否喜欢此博文
        if(authentication != null && authentication.isAuthenticated()) {
            // 上面的username是博主账号。这里需要登录账户
            User loginUser = (User)userDetailsService.loadUserByUsername(authentication.getName());
            int userId = -1;
            if (user != null)
                userId = loginUser.getId();
            int likeStatus = likeService.getLikeStatus(userId, Cons.ENTITY_TYPE.ENTITY_BLOG, id);// id 为 blog id
            if(likeStatus == 1){ // 喜欢
                model.addAttribute("like", "like");
            }
        }
        // 判断访问者是否是博客的所有者
        boolean isBlogOwner = false;
        logger.info("+++++++++++++++++访问博客者权限信息： " + authentication);
        if (authentication !=null && authentication.isAuthenticated()
                &&  !authentication.getPrincipal().toString().equals("anonymousUser")) {
            principal = (User)authentication.getPrincipal();
            if (principal !=null && username.equals(principal.getUsername())) {
                isBlogOwner = true;
            }
        }

        model.addAttribute("isBlogOwner", isBlogOwner);
        model.addAttribute("blogModel",blog);
        model.addAttribute("user", user);

        if(authentication != null) {
            model.addAttribute("username_authenticated", authentication.getName());
        }else{
            model.addAttribute("username_authenticated", "nil");
        }
        return "userspace/blog";
    }

    /**
     * 删除指定博客
     * @param username
     * @param id
     * @return
     */
    @DeleteMapping("/{username}/blogs/{id}")
    @PreAuthorize("authentication.name.equals(#username)")
    public ResponseEntity<ApiResponse> deleteBlog(@PathVariable("username") String username,
                                                  @PathVariable("id") Integer id) {

        try {
            blogService.removeBlog(id);
        } catch (Exception e) {
            return ResponseEntity.ok().body(ApiResponse.ofMessage(500, "删除博客失败"));
        }

        String redirectUrl = "/space/" + username + "/blogs";
        return ResponseEntity.ok().body(ApiResponse.ofSuccess(redirectUrl));
    }

    /**
     * 获取编辑博客的界面
     * @param model
     * @return
     */
    @GetMapping("/{username}/blogs/edit/{id}")
    public ModelAndView editBlog(@PathVariable("username") String username,
                                 @PathVariable("id") Integer id,
                                 Model model,
                                 Authentication authentication) {
        // 获取分类列表
        User user = (User)userDetailsService.loadUserByUsername(username);
        List<Catalog> catalogs = catalogService.listCatalogs(user);

        model.addAttribute("blog", blogService.getBlogById(id));
        model.addAttribute("catalogs", catalogs);
        if(authentication != null) {
            model.addAttribute("username_authenticated", authentication.getName());
        }else{
            model.addAttribute("username_authenticated", "nil");
        }
        model.addAttribute("user", user);
        return new ModelAndView("userspace/blogedit", "blogModel", model);
    }
}
